// !类型断言：警告编译器不准报错

let unknownVar_3: unknown;

// (unknownVar_3 as { foo: () => {} }).foo();

const str: string = "linbudu";

// 还可以 as 到 any 来为所欲为，跳过所有的类型检查：
// (str as any).func().foo().prop;



// 可以在联合类型中断言一个具体的分支：
function foo1(union: string | number) {
  if ((union as string).indexOf('lys')) {

  }
  if ((union as number).toFixed() === '599') {
    console.log(599);
  }
}

// 但是类型断言的正确使用方式是，在 TypeScript 类型分析不正确或不符合预期时，将其断言为此处的正确类型：
interface IFoo {
  name: string;
}
// declare var obj: {
//   foo2: IFoo
// }

var obj = {
  foo2: { name: 'lys' }
}

const { foo2 = {} as IFoo } = obj


//需要注意的是，类型断言应当是在迫不得己的情况下使用的。


// # ---双重断言
const str3: string = "linbudu";
// 从 X 类型 到 Y 类型的断言可能是错误的，blabla
// (str as { handler: () => {} }).handler();  //  指鹿为马太过离谱(会有提醒如下)


//它会提醒你先断言到 unknown 类型，再断言到预期类型
const str3_0: string = "linbudu";

// 1,使用as
(str3_0 as unknown as { handler: () => {} }).handler && (str3_0 as unknown as { handler: () => {} }).handler();
// 2,使用尖括号断言
(<{ handler: () => {} }>(<unknown>str)).handler && (<{ handler: () => {} }>(<unknown>str)).handler();

// 这是因为你的断言类型和原类型的差异太大，需要先断言到一个通用的类，即 any / unknown。这一通用类型包含了所有可能的类型，因此断言到它和从它断言到另一个类型差异不大。

//#-- 非空断言

// 只是foo4的类型声明
declare var foo4: {
  func?: () => ({
    prop?: number | null;
  })
};

// 然后这里查看ts的检查即可，不必运行，因为foo4并未赋值
// foo4.func().prop.toFixed(); //func 在 foo 中不一定存在，prop 在 func 调用结果中不一定存在，且可能为 null，我们就会收获两个类型报错。如果不管三七二十一地坚持调用，想要解决掉类型报错就可以使用非空断言：

// foo4.func!().prop!.toFixed()
// 其应用位置类似于可选链：
// foo4?.func?.().prop?.toFixed();

//但不同的是，非空断言的运行时仍然会保持调用链，因此在运行时可能会报错。而可选链则会在某一个部分收到 undefined 或 null 时直接短路掉，不会再发生后面的调用。

/* 非空断言的常见场景 */
// const element = document.querySelector("#id")!;

const target = [1, 2, 3, 599].find(item => item === 599)!;
console.log("target__", target);

// 为什么说非空断言是类型断言的简写？因为上面的非空断言实际上等价于以下的类型断言操作
// ((foo4.func as () => ({ prop?: number; }))().prop as number).toFixed()

// TODO: non-nullable-type-assertion-style  检查代码中是否存在类型断言能够被简写为非空断言的情况


/*类型断言还有一种用法是作为代码提示的辅助工具 */
interface IStruct {
  // foo: string | number;
  foo: string;
  bar: {
    barPropA: string;
    barPropB: number;
    barMethod: () => void;
    baz: {
      handler: () => Promise<void | number>
    }
  }
}

// 这个时候等待你的是一堆类型报错，你必须规规矩矩地实现整个接口结构才可以。
// const obj_test: IStruct = {};

// 但如果使用类型断言，我们可以在保留类型提示的前提下，不那么完整地实现这个结构：
const obj_test1 = <IStruct>{
  bar: {
    baz: {
      handler: () => Promise.resolve(123)
    }
  },

}

const obj_test2 = <IStruct><unknown>{
  foo: 123,
  bar: {
    baz: {
      handler: undefined
    }
  }
}


const str_test: string = 'linbudu';
// (str as (string | { handler: () => {} }) as { handler: () => {} }).handler()